#pragma once
#include <ostream>

#include "inc_windows_h.hpp"
#include "process_info.hpp"
#include "ptr_bitset_t.hpp"
namespace wxx
{
	struct system_info : boost::noncopyable
	{
		enum processor_architecture
		{
			x86 = PROCESSOR_ARCHITECTURE_INTEL,
			ia64 = PROCESSOR_ARCHITECTURE_IA64,
			amd64 = PROCESSOR_ARCHITECTURE_AMD64,
		};

		system_info(bool native = true) throw(...)
			: m_info(get_info(native))
		{
		}
		
		const ptr_bitset_t active_processors() const
		{
			return ptr_bitset_t(m_info.dwActiveProcessorMask);
		}

#define WXX_sysinf_wrap(NewName, Expr, Type)       \
		Type NewName() const throw()               \
		{                                          \
			return static_cast<Type>(m_info.Expr); \
		}

		WXX_sysinf_wrap(processor_count,        dwNumberOfProcessors,        unsigned int)
		WXX_sysinf_wrap(processor_arch,         wProcessorArchitecture,      enum processor_architecture)
		WXX_sysinf_wrap(processor_level,        wProcessorLevel,             unsigned short)
		WXX_sysinf_wrap(processor_revision,     wProcessorRevision,          unsigned short)
		WXX_sysinf_wrap(page_size,              dwPageSize,                  unsigned int)
		WXX_sysinf_wrap(allocation_granularity, dwAllocationGranularity,     unsigned int) 
		WXX_sysinf_wrap(min_address,            lpMinimumApplicationAddress, void*)
		WXX_sysinf_wrap(max_address,            lpMaximumApplicationAddress, void*)
#undef WXX_sysinf_wrap

	private:
		const SYSTEM_INFO m_info;

		static SYSTEM_INFO get_info(bool native) throw(...)
		{
			SYSTEM_INFO info;

			if (native && process_info::current().is_wow64())
			{
				::GetNativeSystemInfo(&info);
			}
			else
			{
				::GetSystemInfo(&info);
			}

			return info;
		}
	};

	template<class Ch, class Tr>
	std::basic_ostream<Ch, Tr>&
	operator<< (std::basic_ostream<Ch,Tr>& output, const system_info& info)
	{
		using namespace std;

		output << "active_processors:      "   << info.active_processors()      << endl;
		output << "processor_count:        "   << info.processor_count()        << endl;
		output << "processor_arch:         "   << info.processor_arch()         << endl;
		output << "processor_revision:     "   << info.processor_revision()     << endl;
		output << "processor_level:        "   << info.processor_level()        << endl;

		output << "page_size:              "   << info.page_size()              << endl;
		output << "allocation_granularity: "   << info.allocation_granularity() << endl;
		output << "min_address:            0x" << hex << info.min_address()     << endl;
		output << "max_address:            0x" << hex << info.max_address()     << endl;

		return output;
	}

WXX_ENUM_FORMATTER_BEGIN(enum system_info::processor_architecture)
	WXX_ENUM_FORMATTER(system_info::x86, "x86")
	WXX_ENUM_FORMATTER(system_info::ia64, "ia64")
	WXX_ENUM_FORMATTER(system_info::amd64, "amd64")
WXX_ENUM_FORMATTER_END
}